package org.wheel.plugins.toolkit.sql.spi.register;

import org.wheel.plugins.toolkit.sql.spi.annotation.ColumnAnnotation;
import org.wheel.plugins.toolkit.sql.spi.annotation.TableAnnotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

public final class AnnotationRegistry {

    private static final Set<Class<? extends Annotation>> TABLE  = new CopyOnWriteArraySet<>();
    private static final Set<Class<? extends Annotation>> COLUMN = new CopyOnWriteArraySet<>();
    private static final Set<Class<? extends Annotation>> ID     = new CopyOnWriteArraySet<>();

    /* 默认先注册自己的注解，保证开箱即用 */
    static {
        registerTableAnnotation(org.wheel.plugins.toolkit.sql.annon.Table.class);
        registerColumnAnnotation(org.wheel.plugins.toolkit.sql.annon.Column.class);
        registerIdAnnotation(org.wheel.plugins.toolkit.sql.annon.Id.class);
    }

    /* 编程式 API —— 任意模块在启动早期调用即可 */
    public static void registerTableAnnotation(Class<? extends Annotation> anno) {
        TABLE.add(anno);
    }
    public static void registerColumnAnnotation(Class<? extends Annotation> anno) {
        COLUMN.add(anno);
    }
    public static void registerIdAnnotation(Class<? extends Annotation> anno) {
        ID.add(anno);
    }

    /* --------- 内部查询 API —— 以下方法仅供 BeanMeta、FieldMeta 调用 --------- */
    public static Optional<TableAnnotation> findTable(Class<?> clazz) {
        for (Class<? extends Annotation> ac : TABLE) {
            Annotation anno = clazz.getAnnotation(ac);
            if (anno != null) {
                return Optional.of(newTableAdapter(anno)); // 见下方适配器
            }
        }
        return Optional.empty();
    }

    public static Optional<ColumnAnnotation> findColumn(Field field) {
        for (Class<? extends Annotation> ac : COLUMN) {
            Annotation anno = field.getAnnotation(ac);
            if (anno != null) {
                return Optional.of(newColumnAdapter(anno));
            }
        }
        return Optional.empty();
    }

    public static boolean hasId(Field field) {
        for (Class<? extends Annotation> ac : ID) {
            if (field.isAnnotationPresent(ac)) {
                return true;
            }
        }
        return false;
    }

    /* -------------- 适配器工厂 —— 把任意注解包装成“元注解” -------------- */
    private static TableAnnotation newTableAdapter(Annotation origin) {
        return new TableAnnotation() {
            public String value() {
                try {
                    // 统一通过反射取 value() 方法
//                    return (String) origin.annotationType().getMethod("value").invoke(origin);
                    return getAnnotationString(origin, "value");
                } catch (Exception e) {
                    return "";
                }
            }
            public String schema() {
                try {
//                    return (String) origin.annotationType().getMethod("schema").invoke(origin);
                    return getAnnotationString(origin, "schema");
                } catch (Exception e) {
                    return "";
                }
            }
            public Class<? extends Annotation> origin() { return origin.annotationType(); }
            /* 工具：先判断有没有该属性，有才反射，无则返回空串 */
            private String getAnnotationString(Annotation anno, String attr) {
                try {
                    Method m = anno.annotationType().getMethod(attr);
                    Object v = m.invoke(anno);
                    return v == null ? "" : v.toString();
                } catch (NoSuchMethodException e) {
                    // 注解里根本没有这个属性
                    return "";
                } catch (Exception e) {
                    return "";
                }
            }
        };
    }
    private static ColumnAnnotation newColumnAdapter(Annotation origin) {
        return new ColumnAnnotation() {
            public String value() {
                try {
                    return (String) origin.annotationType().getMethod("value").invoke(origin);
                } catch (Exception e) {
                    return "";
                }
            }
            public Class<? extends Annotation> origin() { return origin.annotationType(); }
        };
    }
}